﻿using System;
using System.Net;
using GmailMetroNotifier.Client.Business;
using GmailMetroNotifier.Client.Common;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace GmailMetroNotifier.Tests.Business
{
    [TestClass]
    public class TestGmailManager
    {
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void Test_GetAccountStatus_NullCredentialsThrowsArgumentNullException()
        {
            StubIAtomFeedProvider atomFeedProvider = new StubIAtomFeedProvider();

            GmailManager manager = new GmailManager(atomFeedProvider);

            manager.GetAccountStatus(null);
        }

        [TestMethod]
        public void Test_GetAccountStatus_AttempsAuthenticationTenTimes()
        {
            int retries = 10;

            int counter = 0;

            NetworkCredential inboundCredential = new NetworkCredential(TestScenario.Username, TestScenario.Password);

            EventHandler<AuthenticationFailedEventArgs> eventHandler = (sender, e) =>
            {
                ++counter;
            };

            IAtomFeedProvider atomFeedProvider = new StubIAtomFeedProvider()
            {
                GetAtomFeedAction = c => 
                {
                    throw new AuthenticationFailedException(null, c);
                },
            };

            GmailManager manager = new GmailManager(atomFeedProvider);

            manager.AuthenticationFailed += new WeakEventHandler<object, AuthenticationFailedEventArgs>(eventHandler).Handler;

            AccountStatus status = manager.GetAccountStatus(inboundCredential, retries);

            Assert.IsNull(status);
            Assert.AreEqual(retries + 1, counter);
            Assert.AreEqual(TestScenario.Username, inboundCredential.UserName);
            Assert.AreEqual(TestScenario.Password, inboundCredential.Password);
        }

        [TestMethod]
        public void Test_GetAccountStatus_RetriesAuthenticationTwoTimesByDefault()
        {
            int retries = 2;

            int counter = 0;

            NetworkCredential inboundCredential = new NetworkCredential(TestScenario.Username, TestScenario.Password);

            EventHandler<AuthenticationFailedEventArgs> eventHandler = (sender, e) =>
            {
                ++counter;
            };

            IAtomFeedProvider atomFeedProvider = new StubIAtomFeedProvider()
            {
                GetAtomFeedAction = c =>
                {
                    throw new AuthenticationFailedException(null, c);
                },
            };

            GmailManager manager = new GmailManager(atomFeedProvider);

            manager.AuthenticationFailed += new WeakEventHandler<object, AuthenticationFailedEventArgs>(eventHandler).Handler;

            AccountStatus status = manager.GetAccountStatus(inboundCredential);

            Assert.IsNull(status);
            Assert.AreEqual(retries + 1, counter);
            Assert.AreEqual(TestScenario.Username, inboundCredential.UserName);
            Assert.AreEqual(TestScenario.Password, inboundCredential.Password);
        }

        [TestMethod]
        public void Test_GetAccountStatus_AuthenticationFailedEventArgsAreValid()
        {
            // Retry just one time so we can detect whether we are correctly
            // pasing the values after first attempt
            int retries = 1;

            AuthenticationFailedEventArgs args = null;

            NetworkCredential credential = new NetworkCredential(TestScenario.Username, TestScenario.Password);

            EventHandler<AuthenticationFailedEventArgs> eventHandler = (sender, e) =>
            {
                args = e;
            };

            IAtomFeedProvider atomFeedProvider = new StubIAtomFeedProvider()
            {
                GetAtomFeedAction = c =>
                {
                    throw new AuthenticationFailedException(null, c);
                },
            };

            GmailManager manager = new GmailManager(atomFeedProvider);

            manager.AuthenticationFailed += new WeakEventHandler<object, AuthenticationFailedEventArgs>(eventHandler).Handler;

            AccountStatus status = manager.GetAccountStatus(credential, retries);

            Assert.IsNull(status);
            Assert.AreEqual(TestScenario.Username, args.UserName);
            Assert.AreEqual(TestScenario.Password, args.Password);
            Assert.AreNotSame(credential, args.NewCredential);
            Assert.IsNotNull(args.NewCredential);
            Assert.IsTrue(string.IsNullOrWhiteSpace(args.NewCredential.UserName));
            Assert.IsTrue(string.IsNullOrWhiteSpace(args.NewCredential.Password));
            Assert.IsTrue(string.IsNullOrWhiteSpace(args.NewCredential.Domain));
            Assert.AreEqual(0, args.NewCredential.SecurePassword.Length);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void Test_GetAccountStatus_MinusOneRetryThrowsException()
        {
            int retries = -1;

            NetworkCredential credential = new NetworkCredential();

            IAtomFeedProvider atomFeedProvider = new StubIAtomFeedProvider();

            GmailManager manager = new GmailManager(atomFeedProvider);

            AccountStatus status = manager.GetAccountStatus(credential, retries);
        }

        [TestMethod]
        public void Test_GetAccountStatus_RaisesAuthenticationSucceeded()
        {
            bool authenticationSucceededGotCalled = false;

            NetworkCredential credential = new NetworkCredential();

            EventHandler<AuthenticationSucceededEventArgs> eventHandler = (sender, e) =>
            {
                authenticationSucceededGotCalled = true;
            };

            IAtomFeedProvider atomFeedProvider = new StubIAtomFeedProvider()
            {
                GetAtomFeedAction = c => null,
            };

            GmailManager manager = new GmailManager(atomFeedProvider);

            manager.AuthenticationSucceeded += new WeakEventHandler<object, AuthenticationSucceededEventArgs>(eventHandler).Handler;

            manager.GetAccountStatus(credential);

            Assert.IsTrue(authenticationSucceededGotCalled);
        }

        [TestMethod]
        public void Test_GetAccountStatus_ReturnsValidStatusForValidAtomFeed()
        {
            int fullCount = 5;

            DateTime modifiedDate = new DateTime(2013, 10, 10);

            string atomFeedText = string.Format(TestScenario.AtomFeedFormat, fullCount, modifiedDate);

            NetworkCredential credential = new NetworkCredential();

            IAtomFeedProvider atomFeedProvider = new StubIAtomFeedProvider()
            {
                GetAtomFeedAction = c => atomFeedText,
            };

            GmailManager manager = new GmailManager(atomFeedProvider);

            AccountStatus status = manager.GetAccountStatus(credential);

            Assert.IsNotNull(status);
            Assert.AreEqual(fullCount, status.UnreadCount);
            Assert.AreEqual(modifiedDate, status.ModificationDate);
            Assert.IsTrue(status.IsModified);
        }

        [TestMethod]
        public void Test_GetAccountStatus_SameAtomFeedSetIsModifiedToFalse()
        {
            int fullCount = 5;

            DateTime modifiedDate = new DateTime(2013, 10, 10);

            string atomFeedText = string.Format(TestScenario.AtomFeedFormat, fullCount, modifiedDate);

            NetworkCredential credential = new NetworkCredential();

            IAtomFeedProvider atomFeedProvider = new StubIAtomFeedProvider()
            {
                GetAtomFeedAction = c => atomFeedText,
            };

            GmailManager manager = new GmailManager(atomFeedProvider);

            AccountStatus status = manager.GetAccountStatus(credential);

            status = manager.GetAccountStatus(credential);

            Assert.IsNotNull(status);
            Assert.AreEqual(fullCount, status.UnreadCount);
            Assert.AreEqual(modifiedDate, status.ModificationDate);
            Assert.IsFalse(status.IsModified);
        }
    }
}
